#ifndef BUTTON_H
#define BUTTON_H

#include "widget.h"
#include "../../images_manager.h"
#include "../../anime/utils.h"

#include <math.h>

#include <QImage>

class Button;

class ButtonListener
{
public:
    virtual void OnClick(Button* b, QMouseEvent* e, int rel_x, int rel_y) = 0;
};

class Button: public Widget, public AnimationObserver
{
public:
    /* ---------------------- constructor and destructor --------------------------------------- */
    Button(Widget* _parent, Engine* _engine, QString _text, int _x, int _y, int _w, int _h)
        :Widget(_parent, _engine, _x, _y, _w, _h), penColor(0, 0, 0, 255), text(_text), font("Palatino", 12),
         om_ni(new Power_NI<float>(0.7), true), hoverAnime(_engine->fps, 200), showAnime(_engine->fps, 1000)
    {
        showAlpha = 0;
        showGraph.AddSimpleProperty("showAlpha", showAlpha, &invalidated);
        showGraph.AddSimpleProperty("penAlpha", penAlpha);
        prevAlpha = penColor.alpha();
        showGraph.AddInterpolator(showAnime.GetTime(), 0, 1000, "showAlpha", 0, 255);
        penAlphaId = showGraph.AddInterpolator(showAnime.GetTime(), 0, 1000, "penAlpha", 0, 0);
        showAnime.AddObserver(this);
        showAnime.SetDuration(300);
        engine->AddAnimation(&showAnime);

        component = .0;
        pComponent = new SimpleProperty<float>("pComponent", &component);
        si = new ScalarInterpolator<int, float>(new Power_NI<float>(1), 0, 200, .0, 1.0);
        si->AttachTo(pComponent);
        hoverAnime.Reverse();
        hoverAnime.GetTime()->AddObserver(si);
        hoverAnime.AddObserver(this);
        hoverAnime.SetDuration(150);
        engine->AddAnimation(&hoverAnime);

        RecalculateImages();
        RecalculateCentre();

        pressed = false;
        onMe = false;
        mouseTracking = true;

        SetFont(font);
    }

    virtual ~Button(){}
    /* ----------------------------------------------------------------------------------------- */

    /* ------------------------ public functions ----------------------------------------------- */
    // size handling
    virtual void SetWidth(int newWidth)
    {
        Widget::SetWidth(newWidth);
        RecalculateImages();
        RecalculateCentre();
    }

    virtual void SetHeight(int newHeight)
    {
        Widget::SetHeight(newHeight);
        RecalculateImages();
        RecalculateCentre();
    }

    virtual void SetX(int newX)
    {
        Widget::SetX(newX);
        RecalculateCentre();
    }

    virtual void SetY(int newY)
    {
        Widget::SetY(newY);
        RecalculateCentre();
    }

    // paint function
    virtual void Paint(QPainter &painter)
    {
        if(pressed)
        {
            painter.drawImage(x, y, pressedImage);
        }else
        {
            ImageTransition(component);
            ImageUtils::BlendCheck(backImage, showAlpha);
            painter.drawImage(x, y, backImage);
        }
        penColor.setAlpha(penAlpha);
        painter.setPen(penColor);
        painter.drawText(x + textX, y + textY, text);
    }

    // visibility handling
    virtual void doShow()
    {
        showAnime.Stop();
        showGraph.Get(penAlphaId)->SetMaxY(prevAlpha);
        paintEnabled = true;
        showAnime.Play();
    }

    virtual void doHide()
    {
        showAnime.Reverse(true);
        visible = false;
        mouseTracking = false;
        showAnime.Play();
    }

    // font handling
    virtual void SetFont(const QFont& _font)
    {
        font = _font;
        RecalculateTextPosition();
    }

    virtual QColor getColor(){ return penColor; }

    virtual void SetColor(const QColor& c)
    {
        penColor = c;
        prevAlpha = c.alpha();
    }

    // text handling
    QString GetText(){ return text; }

    virtual void SetText(const QString& newText)
    {
        text = newText;
        RecalculateTextPosition();
    }
    /* ----------------------------------------------------------------------------------------- */

    /* ------------------------ AnimationObserver implementation ------------------------------- */
    virtual void OnPause(Animation* a){};
    virtual void OnStop(Animation* a){};

    virtual void OnEnd(Animation* a)
    {
        if(a == &showAnime)
        {
            if(a->IsReversed())
            {   // hiding
                paintEnabled = false;
                Hidden();
            }else
            {   // showing
                visible = true;
                mouseTracking = true;
                Showed();
            }
        }
    };

    virtual void OnPlay(Animation* a){};
    /* ----------------------------------------------------------------------------------------- */

    /* --------------------------- mouse events handling --------------------------------------- */
    virtual void OnMouseMoved(QMouseEvent *e, bool onWidget, int x_rel, int y_rel)
    {
        if(onMe != onWidget)
        {
            onMe = onWidget;
            hoverAnime.Reverse();
            hoverAnime.Play();
        }
    }

    virtual void OnMousePressed(QMouseEvent *e, bool inside, int x_rel, int y_rel)
    {
        pressed = inside;
    }

    virtual void OnMouseReleased(QMouseEvent *e, bool inside, int x_rel, int y_rel)
    {
        pressed = false;
    }

    virtual void OnMouseClick(QMouseEvent *e, int x_rel, int y_rel)
    {
        for(vector< ButtonListener* >::iterator i=bListeners.begin(); i!=bListeners.end(); ++i)
        {
            (*i)->OnClick(this, e, x_rel, y_rel);
        }
    }
    /* ----------------------------------------------------------------------------------------- */

    /* ---------------------- ButtonListeners managing ----------------------------------------- */
    virtual void AddButtonListener(ButtonListener* bl)
    {
        bListeners.push_back(bl);
    }

    virtual void ClearListeners()
    {
        bListeners.clear();
    }
    /* ----------------------------------------------------------------------------------------- */

protected:
    QColor penColor;
    QString text;
    QFont font;

    QImage normalImage;
    QImage hoverImage;
    QImage pressedImage;

    QImage backImage;

    /* ----------------------- protected functions --------------------------------------------- */
    // recalculates the images of the button
    void RecalculateImages()
    {
        QImage* originalNormalImage = ImagesManager::GetInstance()->GetImage(ImagesManager::BUTTON_NORMAL);
        normalImage = originalNormalImage->scaled(w, h);

        QImage* originalHoverImage = ImagesManager::GetInstance()->GetImage(ImagesManager::BUTTON_HOVER);
        hoverImage = originalHoverImage->scaled(w, h);

        QImage* originalPressedImage = ImagesManager::GetInstance()->GetImage(ImagesManager::BUTTON_PRESSED);
        pressedImage = originalPressedImage->scaled(w, h);

        backImage = normalImage.copy(0, 0, w, h);
    }

    // recalculates the button center
    void RecalculateCentre()
    {
        buttonCentreX = x + w/2;
        buttonCentreY = y + h/2;
    }

    void RecalculateTextPosition()
    {
        int textWidth = ImageUtils::GetTextWidth(text, font);
        int textHeight = ImageUtils::GetTextHeight(text, font);

        textX = (w - textWidth)/2;
        textY = (h - textHeight)/2 + textHeight;
    }
    /* ----------------------------------------------------------------------------------------- */

private:
    float component;
    OneMinus_NI<float> om_ni;
    Property<float>* pComponent;
    ScalarInterpolator<int, float>* si;
    Animation hoverAnime;

    IntGraph showGraph;
    Animation showAnime;
    int showAlpha;

    int buttonCentreX;
    int buttonCentreY;

    int textX;
    int textY;

    bool pressed;
    bool onMe;

    int penAlpha;
    int prevAlpha;
    int penAlphaId;

    vector< ButtonListener* > bListeners;

    inline void ImageTransition(float normalizedValue)
    {
        QRgb normalRgb;
        QRgb finalRgb;
        QRgb interpolatedRgb;
        int r, g, b, a;

        for(int j=0; j<h; j++)
        {
            for(int i=0; i<w; i++)
            {
                normalRgb = normalImage.pixel(i, j);
                finalRgb = hoverImage.pixel(i, j);

                r = InterpolateBetween(qRed(normalRgb), qRed(finalRgb), normalizedValue);
                g = InterpolateBetween(qGreen(normalRgb), qGreen(finalRgb), normalizedValue);
                b = InterpolateBetween(qBlue(normalRgb), qBlue(finalRgb), normalizedValue);
                a = InterpolateBetween(qAlpha(normalRgb), qAlpha(finalRgb), normalizedValue);

                interpolatedRgb = qRgba(r, g, b, a);
                backImage.setPixel(i, j, interpolatedRgb);
            }
        }
    }

    inline int InterpolateBetween(int start, int end, float normalizedValue)
    {
        return start * (1 - normalizedValue) + end * normalizedValue;
    }
};

#endif // BUTTON_H
